﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Drawing;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Windows.Forms;

namespace Marmat.Forms.Skin
{
    /// <summary>
    /// 显示标记的每个表示项的分层集合 Marmat.Forms.Skin.TreeNode。
    /// </summary>
    public partial class TreeView : ScrollControl
    {
        private bool beginUpdate = false;
        private TreeNodeCollection nodes;
        private int itemHeight = -1;
        private List<KeyValuePair<TreeNode, Rectangle>> expanded_RectangleCollection = new List<KeyValuePair<TreeNode, Rectangle>>();
        private List<KeyValuePair<TreeNode, Rectangle>> check_RectangleCollection = new List<KeyValuePair<TreeNode, Rectangle>>();
        private List<KeyValuePair<TreeNode, Rectangle>> treeNode_RectangleCollection = new List<KeyValuePair<TreeNode, Rectangle>>();

        public TreeView()
        {
            SetStyle(ControlStyles.Selectable, true);
            SetStyle(ControlStyles.ResizeRedraw, true); //尺寸变化后绘制
            SetStyle(ControlStyles.Opaque, false); //改善绘图质量
            SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
            SetStyle(ControlStyles.AllPaintingInWmPaint, true);
            SetStyle(ControlStyles.UserPaint, true);
            SetStyle(ControlStyles.DoubleBuffer, true);
            this.BackColor = Color.White;
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);

            int index = 0;
            int height = 0;
            int top = -this.VerticalScroll.Value;
            int scroll = this.VerticalScroll.Value;
            check_RectangleCollection.Clear();
            treeNode_RectangleCollection.Clear();
            expanded_RectangleCollection.Clear();

            // 清除节点所有的矩形信息。
            var allNodeCollection = GetALLNodeCollection(this.Nodes);
            allNodeCollection.ForEach(node => node.bounds = default);

            // 获取需要汇聚的节点。
            TreeNode[] nodeCollection = GetNodeCollection(this.Nodes);
            foreach (var node in nodeCollection)
            {
                var eventArgs_0 = new MeasureTreeNodeEventArgs(node, new Size(TextRenderer.MeasureText(node.Text, this.Font).Width, this.ItemHeight), TreeNodeStates.Default);
                this.OnMeasureNode(eventArgs_0);

                int bottom = height + eventArgs_0.Size.Height;
                if (bottom > scroll && height < scroll + this.Height)
                {
                    this.drawNode(e, eventArgs_0, top + height, index);
                }
                index++;
                height += eventArgs_0.Size.Height;
            }
            this.AutoScrollMinSize = new Size(0, height + 1);

            // 临时函数，获取所有需要绘制的节点。
            static TreeNode[] GetNodeCollection(TreeNodeCollection nodeCollection)
            {
                List<TreeNode> nodes = new List<TreeNode>();
                foreach (TreeNode node in nodeCollection)
                {
                    nodes.Add(node);
                    if (node.IsExpanded && node.Nodes.Count > 0)
                        nodes.AddRange(GetNodeCollection(node.Nodes));
                }
                return nodes.ToArray();
            }

            // 临时函数，获取所有的节点。
            static List<TreeNode> GetALLNodeCollection(TreeNodeCollection nodeCollection)
            {
                List<TreeNode> nodes = new List<TreeNode>();
                foreach (TreeNode node in nodeCollection)
                {
                    nodes.Add(node);
                    if (node.Nodes.Count > 0)
                        nodes.AddRange(GetNodeCollection(node.Nodes));
                }
                return nodes;
            }
        }

        private protected void drawNode(PaintEventArgs e, MeasureTreeNodeEventArgs args, int top, int index)
        {
            int left = 0;
            TreeNode node = args.Node;
            var bounds = this.DrawMode switch
            {
                TreeViewDrawMode.Normal => new Rectangle(this.Indent * (node.Level), top, this.Indent + 5, args.Size.Height),
                TreeViewDrawMode.OwnerDrawText => new Rectangle(this.Indent * (node.Level), top, this.Indent + 5, args.Size.Height),
                TreeViewDrawMode.OwnerDrawAll => new Rectangle(0, top, this.Width, args.Size.Height),
                _ => throw new NotImplementedException(),
            };


            if (this.DrawMode == TreeViewDrawMode.Normal || this.DrawMode == TreeViewDrawMode.OwnerDrawText)
            {
                if (this.ShowPlusMinus)
                    left = bounds.Right;
                if (this.ShowLines)
                {
                    // 绘制节点的连接线条。
                    int lines_left = bounds.Right - 9;
                    using var pen = new Pen(Color.Black);
                    pen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dot;
                    if (index == 0)
                        e.Graphics.DrawLine(pen, lines_left, bounds.Top + (bounds.Height / 2), lines_left, bounds.Bottom);
                    else if (node.NextNode is null)
                        e.Graphics.DrawLine(pen, lines_left, bounds.Top, lines_left, bounds.Top + (bounds.Height) / 2);
                    else
                        e.Graphics.DrawLine(pen, lines_left, bounds.Top, lines_left, bounds.Bottom);
                    e.Graphics.DrawLine(pen, lines_left + 2, bounds.Top + (bounds.Height / 2), bounds.Right, bounds.Top + (bounds.Height / 2));

                    TreeNode parent = node.Parent;
                    for (int i = node.Level; i > 0; i--)
                    {
                        if (parent.NextNode is not null)
                            e.Graphics.DrawLine(pen, (parent.Level + 1) * this.Indent - 4, bounds.Top, (parent.Level + 1) * this.Indent - 4, bounds.Bottom);
                        parent = parent.Parent;
                    }
                }
                // 绘制节点的折叠状体操作图标。
                if (this.ShowPlusMinus && node.Nodes.Count > 0)
                {
                    Image expanded_img;
                    if (node.IsExpanded)
                        expanded_img = Properties.Resources.treeNode_expanded_True;
                    else
                        expanded_img = Properties.Resources.treeNode_expanded_False;
                    var expanded_rect = new Rectangle(bounds.Right - 9 - 4, bounds.Y + (bounds.Height - expanded_img.Height) / 2, expanded_img.Width, expanded_img.Height);
                    e.Graphics.DrawImage(expanded_img, expanded_rect);
                    expanded_RectangleCollection.Add(new KeyValuePair<TreeNode, Rectangle>(node, expanded_rect));
                }

                // 绘制选择框
                if (this.CheckBoxes)
                {
                    Rectangle rect = new Rectangle(left, top, 17, args.Size.Height);
                    left = rect.Right;
                    Image check_img;
                    if (node == MouseMoveNode)
                    {
                        if (node.Checked)
                            check_img = Properties.Resources.Checked_True_highlight;
                        else
                            check_img = Properties.Resources.Checked_False_hightlight;
                    }
                    else
                    {
                        if (node.Checked)
                            check_img = Properties.Resources.Checked_True_normal;
                        else
                            check_img = Properties.Resources.Checked_False_normal;
                    }
                    var check_rect = new Rectangle(rect.X, rect.Y + (rect.Height - check_img.Height) / 2, check_img.Width, check_img.Height);
                    e.Graphics.DrawImage(check_img, check_rect);
                    check_RectangleCollection.Add(new KeyValuePair<TreeNode, Rectangle>(node, check_rect));
                }
            }
            if (this.DrawMode == TreeViewDrawMode.Normal)
            {
                bounds = new Rectangle(left, top, args.Size.Width, args.Size.Height);
                drawText(bounds);
            }
            else
            {
                bounds = new Rectangle(left, top, args.Size.Width, args.Size.Height);
                TreeNodeStates states = TreeNodeStates.Default;
                if (this.selectedNode == node)
                    states = TreeNodeStates.Selected;
                else if (node.Checked)
                    states = TreeNodeStates.Checked;
                else if (this.MouseMoveNode == node)
                    states = TreeNodeStates.Hot;
                var eventArgs_1 = new DrawTreeNodeEventArgs(e.Graphics, node, bounds, states);
                this.OnDrawNode(eventArgs_1);
                if (eventArgs_1.DrawDefault)
                    drawText(bounds);
            }
            node.bounds = bounds;
            treeNode_RectangleCollection.Add(new KeyValuePair<TreeNode, Rectangle>(node, bounds));

            void drawText(Rectangle bounds)
            {
                // 绘制节点文本。
                Font font;
                Color foreColor;
                Color backColor;
                if (this.SelectedNode == node)
                {
                    foreColor = Color.White;
                    backColor = SystemColors.Highlight;
                }
                else
                {
                    foreColor = Color.Black;
                    backColor = Color.Transparent;
                }
                if (node.NodeFont is null)
                    font = this.Font;
                else
                    font = node.NodeFont;
                if (this.MouseMoveNode == node)
                    font = new Font(font, font.Style | FontStyle.Underline);
                TextRenderer.DrawText(e.Graphics, node.Text, font, bounds, foreColor, backColor, TextFormatFlags.Left | TextFormatFlags.VerticalCenter);
            }
        }

        protected override void OnMouseDown(MouseEventArgs e)
        {
            base.OnMouseDown(e);
            if (e.Button == MouseButtons.Left)
            {
                var treeNode = this.expanded_RectangleCollection.FirstOrDefault(x => x.Value.Contains(e.Location)).Key;
                if (treeNode is not null)
                {
                    if (treeNode.IsExpanded)
                    {
                        TreeViewCancelEventArgs args = new TreeViewCancelEventArgs(treeNode, false, TreeViewAction.ByMouse);
                        this.OnBeforeCollapse(args);
                        if (!args.Cancel)
                        {
                            treeNode.Collapse();
                            this.OnAfterCollapse(new TreeViewEventArgs(treeNode));
                        }
                    }
                    else
                    {
                        TreeViewCancelEventArgs args = new TreeViewCancelEventArgs(treeNode, false, TreeViewAction.ByMouse);
                        this.OnBeforeExpand(args);
                        if (!args.Cancel)
                        {
                            treeNode.Expand();
                            this.OnAfterExpand(new TreeViewEventArgs(treeNode));
                        }
                    }
                    return;
                }
                treeNode = this.check_RectangleCollection.FirstOrDefault(x => x.Value.Contains(e.Location)).Key;
                if (treeNode is not null)
                {
                    TreeViewCancelEventArgs args = new TreeViewCancelEventArgs(treeNode, false, TreeViewAction.ByMouse);
                    this.OnBeforeCheck(args);
                    if (!args.Cancel)
                    {
                        treeNode.Checked = !treeNode.Checked;
                        this.OnAfterCheck(new TreeViewEventArgs(treeNode));
                    }
                    return;
                }
                treeNode = this.treeNode_RectangleCollection.FirstOrDefault(x => x.Value.Contains(e.Location)).Key;
                if (treeNode is not null)
                {
                    TreeViewCancelEventArgs args = new TreeViewCancelEventArgs(treeNode, false, TreeViewAction.ByMouse);
                    this.OnBeforeSelect(args);
                    if (!args.Cancel)
                    {
                        this.SelectedNode = treeNode;
                        this.OnAfterSelect(new TreeViewEventArgs(treeNode));
                    }
                    return;
                }
            }
        }

        protected override void OnMouseClick(MouseEventArgs e)
        {
            base.OnMouseClick(e);
            var treeNode = this.treeNode_RectangleCollection.FirstOrDefault(x => x.Value.Contains(e.Location)).Key;
            if (treeNode is not null)
                this.OnNodeMouseClick(new TreeNodeMouseClickEventArgs(treeNode, e.Button, e.Clicks, e.X, e.Y));
        }

        protected override void OnMouseDoubleClick(MouseEventArgs e)
        {
            base.OnMouseDoubleClick(e);
            var treeNode = this.treeNode_RectangleCollection.FirstOrDefault(x => x.Value.Contains(e.Location)).Key;
            if (treeNode is not null)
                this.OnNodeMouseDoubleClick(new TreeNodeMouseClickEventArgs(treeNode, e.Button, e.Clicks, e.X, e.Y));
        }

        protected override void OnMouseHover(EventArgs e)
        {
            base.OnMouseHover(e);

            Point location = Control.MousePosition;
            var treeNode = this.treeNode_RectangleCollection.FirstOrDefault(x => x.Value.Contains(location)).Key;
            if (treeNode is not null)
                this.OnNodeMouseHover(new TreeNodeMouseHoverEventArgs(treeNode));
        }

        protected override void OnMouseMove(MouseEventArgs e)
        {
            base.OnMouseMove(e);
            var treeNode = this.treeNode_RectangleCollection.FirstOrDefault(x => x.Value.Contains(e.Location)).Key;
            if (treeNode is null)
                treeNode = this.check_RectangleCollection.FirstOrDefault(x => x.Value.Contains(e.Location)).Key;
            if (MouseMoveNode != treeNode)
            {
                if (treeNode is not null)
                    MouseMoveNode = treeNode;
                else if (MouseMoveNode is not null)
                    MouseMoveNode = null;
                this.Invalidate();
            }
        }

        protected override void OnMouseLeave(EventArgs e)
        {
            base.OnMouseLeave(e);
            MouseMoveNode = null;
            this.Invalidate();
        }

        protected override void OnInvalidated(InvalidateEventArgs e)
        {
            //if (!this.beginUpdate)
            //    base.OnInvalidated(e);
        }
    }
}
